设计模式教程(1) - 对象创建型模式

简单工厂

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class ConcreteProductA extends Product{
@Override
public void operation() {
System.out.println("简单工厂A");
}
}
class ConcreteProductB extends Product{
@Override
public void operation() {
System.out.println("简单工厂A");
}
}
class SimpleFactory{
public Product newInstance(String type){
if(type == null){
return null;
}else if(type.equals("a")){
return new ConcreteProductA();
}else if(type.equals("b")){
return new ConcreteProductB();
}else{
return null;
}
}
}

建造者模式

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

适用场景

主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化

建造者模式的好处是的建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了

结构图

建造者模式

代码实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class Products{
private List<String> parts = new ArrayList<String>();
parts.add(part);
}
public void show(){
System.out.println("====产品详细信息start====");
for(String part : parts){
System.out.println(part);
}
System.out.println("====产品详细信息end====");
}
}
abstract class Builder{
public abstract void BuilPartA();
public abstract void BuilPartB();
public abstract Products getProduct();
}
class ConcreteBuilderA extends Builder{
private Products products = new Products();
@Override
public void BuilPartA() {
products.add("添加组件a-1");
}
@Override
public void BuilPartB() {
products.add("添加组件a-1");
}
@Override
public Products getProduct() {
return products;
}
}
class ConcreteBuilderB extends Builder{
private Products products = new Products();
@Override
public void BuilPartA() {
products.add("添加组件b-1");
}
@Override
public void BuilPartB() {
products.add("添加组件b-2");
}
@Override
public Products getProduct() {
return products;
}
}
class Director {
public void Construct(Builder builder){
builder.BuilPartA();
builder.BuilPartB();
}
}
public class BuilderTest {
public static void main(String[] args) {
Director director = new Director();
Builder buildera = new ConcreteBuilderA();
Builder builderb = new ConcreteBuilderB();
director.Construct(buildera);
buildera.getProduct().show();
director.Construct(builderb);
builderb.getProduct().show();
}
}

单例模式

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点

代码实例

首先写一个简单的单例类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static Singleton instance = null;
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 静态工程方法,创建实例 */
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return instance;
}
}

以上简单的单例类在多线程并发的时候就会出问题,针对多线程并发访问的环境下对getInstance()方法进行加锁

1
2
3
4
5
6
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

以上代码解决了并发访问的问题,但是性能有所下降,因为每次访问getInstance()都会加锁,而实际上只需要当instance为空的时候才需要加锁,所以进行以下优化

1
2
3
4
5
6
7
public static Singleton getInstance(){
if(instance == null){
synchornized(this){
instance = new Singleton();
}
}
}

上线代码貌似既解决了并发又解决了性能,但实际上仍然是由问题的,因为在java中创建对象和赋值是分开进行的,即instance = new Singleton();是分两步执行的,并且JVM并不能保证这两步操作的先后顺序。
也就说有可能jvm先为新的Singleton实例分配一段内存空间赋值给instance,然后再再创建Singleton实例,这样就可能出错了,我们以A、B两个线程为例:

  1. A、B线程同时进入了第一个if判断。
  2. A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
  3. 由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
  4. B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
  5. 此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

对改程序做进一步优化:

1
2
3
4
5
6
private static class SingletonFactory{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonFactory.instance;
}

另外还有一种解决办法就是利用静态内部类来实现,jvm机制可以保证当一个类被加载时,这个类的加载过程是线程互斥的,这样就可以利用这个特性,这样当我们第一次调用getInstance()时,JVM可以保证我们在调用过程中的instance只会被创建一次,代码如下:

1
2
3
4
5
6
7
8
9
/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
}
/* 获取实例 */
public static Singleton getInstance() {
return SingletonFactory.instance;
}

工厂方法

定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到子类

适用场景

工厂方法适用于以下几种场景

  • 当一个类不知道它所必须创建的对象类的时候
  • 当一个类希望由它的子类来指定它所创建的对象的时候
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候

结构图

工厂方法模式

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
abstract class Product{
public abstract void operation();
}
class ConcreteProduct extends Product{
public void operation() {
System.out.println("具体被创建类的操作");
}
}
abstract class Creator{
public abstract Product FactoryMethod();
}
class ConcreteCreateor extends Creator{
public Product FactoryMethod() {
return new ConcreteProduct();
}
}
public class FactoryMethodTest {
public static void main(String[] args) {
}
}

抽象工厂模式

定义

提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类

抽象工厂与工厂方法的区别

工厂方法模式:

  • 一个抽象产品类,可以派生出多个具体产品类。
  • 一个抽象工厂类,可以派生出多个具体工厂类。
  • 每个具体工厂类只能创建一个具体产品类的实例。

抽象工厂模式:

  • 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
  • 一个抽象工厂类,可以派生出多个具体工厂类。
  • 每个具体工厂类可以创建多个具体产品类的实例,也就是创建的是一个产品线下的多个产品。

工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

工厂方法只需创建一种产品,他的着重点在于”怎么创建”产品上,也就是说如果你的大量代码很可能围绕着这种产品的构造,初始化这些细节上面。也因为如此,类似的产品之间有很多可以复用的特征,所以会和模版方法相随。

抽象工厂需要创建一些列产品,着重点在于”创建哪些”产品上,也就是说如果你的主要任务是划分不同差异的产品线,并且尽量保持每条产品线接口一致,从而可以从同一个抽象工厂继承。

例子:

一个抽象服装店类可以出售裤子和衣服两个产品。

一个抽象裤子工厂有且只有一条生产线专门生产裤子;

一个裤子抽象产品类有耐克和阿迪达斯两个具体产品类,

一个抽象衣服工厂有且只有一套生产线专门生产衣服

衣服抽象产品类有柒牌和劲霸男装有个具体产品类,

具体服装店A出售柒牌衣服+耐克裤子

具体服装店B出售劲霸衣服+阿迪达斯裤子

这里的服务装店对应的就是抽象工厂,衣服和裤子工厂及其生产线对应的是工厂方法

原型模式

定义

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象[设计模式]

浅复制与深复制

浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。

简单来说,深复制进行了完全彻底的复制,而浅复制不彻底。

适用场景

使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。

使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。

因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,而且可以使系统的整体性能提高很多。

-------------本文到此结束,感谢您的阅读-------------